#include "user/socket.h"
#include "user/thread.h"

#include <iostream>
#include <string>

class server : public user::thread
{
	SOCKET sock;
	
	public:
		server() {}
		~server() {}

	protected:
		void execute(void*)
		{
			if ((sock = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0)
				{ std::cout << "server: socket() failed\n"; return; }

			// construct local address struct
			struct sockaddr_in local_addr;
			memset(&local_addr,0,sizeof(local_addr));
			local_addr.sin_family = AF_INET;
			local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
			local_addr.sin_port = htons(6543);

			// bind to the local addr
			if (bind(sock,(struct sockaddr*) &local_addr, sizeof(local_addr)) < 0)
				{ std::cout << "server: bind() failed\n"; return; }

			// mark as listening socket
			if (listen(sock, 5) < 0)
				{ std::cout << "server: listen() failed\n"; return; }

			for (;;)
			{
				struct sockaddr_in client_addr;
				socklen_t client_addr_len = sizeof(client_addr);
				SOCKET client_sock;

				std::cout << "server: waiting for a connection\n";

				// wait for client to connect
				if ((client_sock = accept(sock,(struct sockaddr*) &client_addr, &client_addr_len)) < 0)
					{ std::cout << "server: accept() failed\n"; return; }

				// connected!
				std::cout << "server: connection from " << inet_ntoa(client_addr.sin_addr) << "\n";

				// bye
				closesocket(client_sock);
			}
		}
};

class client : public user::thread
{
	SOCKET sock;
	std::string host;				// dotted quad ipno

	public:
		client(std::string s) { host = s; }
		~client() {}

	protected:
		void execute(void*)
		{
			struct sockaddr_in addr;

			memset(&addr,0,sizeof(addr));
			addr.sin_family = PF_INET;
			addr.sin_port = htons(6543);

			// resolve
			{
				unsigned long quad;
				
				if ((quad=inet_addr(host.c_str())) != INADDR_NONE)
					memcpy(&addr.sin_addr,&quad,sizeof(addr.sin_addr));
				else
				{
					struct hostent* hp;

					if ((hp=gethostbyname(host.c_str())) == 0)
						{ std::cout << "client: cannot resolve " << host << "\n"; return; }
					memcpy(&addr.sin_addr,hp->h_addr_list[0],sizeof(addr.sin_addr));
				}
			}

			// create the socket
			if ((sock=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0)
				{ std::cout << "client: socket() failed\n"; return; }

			// connect to the host
			if (connect(sock,(struct sockaddr*) &addr, sizeof(addr)) < 0)
				{ std::cout << "client: connect() failed\n"; return; }

			// connected!
			std::cout << "client: connected to host " << host << "\n";

			// bye
			std::cout << "client: all went well, exiting thread\n";
			closesocket(sock);
		}
};



int main(int argc,char* argv[])
{
	user::wsa_startup();

	if (argc != 2) {
		std::cout << "usage: " << argv[0] << " SERVER-IP\nSpecify the server the client should connect to\n";
		return 1;
	}
	
	server s;
	client c(argv[1]);

	s.run(0);
	c.run(0);

	for (;;) ;
	
	return 0;
}

